package database.viewer;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;

import database.table.DBTableData;

/**
 * This panel contains the table of desired tables,
 * loaded or not. Also, shows the number of elements
 * if loaded.
 * 
 * @author Hossomi
 *
 */

public class TableListPanel extends JPanel implements ListSelectionListener {
	private JTable 			table;		// The table
	private TableListModel	model;		// The table model
	private JScrollPane		scroll;		// Scroll pane to insert table
	
	private JTextField		addText;	// Text field to write the DBTable name to add
	private JButton			buttonAdd;	// Button to add DBTable 
	
	private ActionListener	control;
	
	public TableListPanel( ActionListener control ) {
		GridBagConstraints constraints = new GridBagConstraints();
		
		setLayout( new GridBagLayout() );
		setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

		this.control = control;
		
		// Setup table
		model = new TableListModel();
		table = new JTable(model);
		table.getSelectionModel().addListSelectionListener(this);
		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		scroll = new JScrollPane(table);
		scroll.getViewport().add(table);
		
		// Setup general constraints
		constraints.fill = GridBagConstraints.BOTH;
		
		// Add scroll pane with table
		constraints.gridx = 0;
		constraints.gridy = 0;
		constraints.gridwidth = 2;
		constraints.weighty = 1.0;
		add(scroll, constraints);
		
		// Add text field
		constraints.gridx = 0;
		constraints.gridy = 1;
		constraints.gridwidth = 1;
		constraints.weightx = 0.9;
		constraints.weighty = 0.0;
		addText = new JTextField();
		addText.setFocusable(true);
		addText.addKeyListener( new KeyListener() {
			
			@Override
			public void keyTyped(KeyEvent e) {
				if (e.getKeyChar() == KeyEvent.VK_ENTER) {
					addTableFromText();
				}
			}
			
			@Override
			public void keyReleased(KeyEvent e) {}
			
			@Override
			public void keyPressed(KeyEvent e) {}
		});
		add(addText, constraints);
		
		// Add button
		constraints.gridx = 1;
		constraints.gridy = 1;
		constraints.weightx = 0.1;
		buttonAdd = new JButton("Add");
		buttonAdd.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				addTableFromText();
			}
		});

		add(buttonAdd, constraints);
	}
	
	public boolean addTableFromText() {
		String text = addText.getText();
		
		if (text.trim().length() > 0) {
			addText.setText("");
			return addTable(text);
		}
		
		return false;
	}
	
	/**
	 * Adds a new DBTable by its name. It will not be loaded yet.
	 * @param tableName The name of the table from the database
	 * @return True if new DBTable is not already in the table
	 */
	public boolean addTable( String tableName ) {
		if ( model.addRow(tableName) ) {
			table.getSelectionModel().setSelectionInterval(0, 0);
			return true;
		}
		
		return false;
	}
	
	public int getTableIndex( String name ) {
		for (int row = 0; row < model.getRowCount(); row++)
			if (model.getValueAt(row, 0).equals(name))
				return row;
		
		return -1;
	}
	
	public String getTable( int i ) {
		try {
			return model.getValueAt(i, 0).toString();
		}
		catch (IndexOutOfBoundsException e) {
			return null;
		}
	}
	
	public String getSelectedTable() {
		int row = table.getSelectedRow();
		if (row >= 0)
			return model.getValueAt( row, 0 ).toString();
		
		return null;
	}
	
	public int getTableCount() {
		return model.getRowCount();
	}
	
	
	public boolean assignTableData( int row, DBTableData data ) {
		if ( model.assignTableData(row, data) ) {
			table.repaint();
			return true;
		}
		
		return false;
	}
	
	public boolean assignTableData( String name, DBTableData data ) {
		return assignTableData( getTableIndex(name), data);
	}
	
	public boolean isTableLoaded( String tableName ) {
		int row = getTableIndex(tableName);
		return model.isLoaded(row);
	}
	
	@Override
	public void valueChanged(ListSelectionEvent arg0) {
		ActionEvent event = new ActionEvent(this, 0, DBTableViewer.COMMAND_TABLECHANGE);
		control.actionPerformed(event);
	}
	
	/**
	 * Custom table model for this table. Basically consists of two
	 * columns with DBTable name and element count (which will
	 * display other information if not available).
	 * 
	 * @author Hossomi
	 *
	 */
	public static class TableListModel extends AbstractTableModel {
		List<TableListEntry> entries;
		
		static String COL_NAMES[] = {"Table", "Elements"};
		
		public TableListModel() {
			entries = new ArrayList<TableListEntry>();
		}
		
		/**
		 * Adds a new row (not loaded) if the name is not already
		 * in the table.
		 * @param name The name of the table from the database.
		 * @return True if insertion was successful, that is, the
		 * DBTable name is unique in this table.
		 */
		public boolean addRow( String name ) {
			TableListEntry e = new TableListEntry(name);
			
			if (!entries.contains(e)) {
				entries.add(e);
				fireTableStructureChanged();
				return true;
			}
			
			return false;
		}
		
		@Override
		public String getColumnName(int column) {
			if (column < 0 || column >= 2)
				return null;
			
			return COL_NAMES[column];
		}
		
		@Override
		public int getColumnCount() {
			return COL_NAMES.length;
		}

		@Override
		public int getRowCount() {
			return entries.size();
		}

		@Override
		public Object getValueAt(int row, int col) {
			try {
				TableListEntry entry = entries.get(row);
				switch (col) {
					case 0: return entry.name;
					case 1: return (entry.data != null) ? (entry.data.getRowCount()) : "Not loaded";
				}
			}
			catch (IndexOutOfBoundsException e) {
				System.out.println("[TableListPanel] ERROR: index " + row + " out of bounds");
				return null;
			}
			
			System.out.println("[TableListPanel] ERROR: column " + col + " does not exist");
			return null;
		}
		
		public boolean assignTableData( int row, DBTableData data ) {
			try {
				TableListEntry entry = entries.get(row);
				entry.data = data;
				return true;
			}
			catch (IndexOutOfBoundsException e) {
				System.out.println("[TableListPanel] ERROR: index " + row + " out of bounds");
				return false;
			}
		}
		
		public boolean isLoaded( int row )  {
			try {
				TableListEntry entry = entries.get(row);
				return entry.data != null;
			}
			catch (IndexOutOfBoundsException e) {
				System.out.println("[TableListPanel] ERROR: index " + row + " out of bounds");
				return false;
			}
		}
	}
	
	public static class TableListEntry {
		String	name;
		DBTableData	data;
		int		elements;
		boolean	loaded;
		
		public TableListEntry( String name ) {
			this.name = name;
			this.elements = 0;
			this.loaded = false;
			this.data = null;
		}
		
		@Override
		public boolean equals(Object obj) {
			if (obj != null && obj instanceof TableListEntry)
				return name.equals(((TableListEntry) obj).name);
			
			return false;
		}
	}
}
